/**
 *
 */
package ua.at.tsvetkov.samizdat.data;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.ref.SoftReference;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;

import ua.at.tsvetkov.net.NetCheck;
import ua.at.tsvetkov.samizdat.Const;
import ua.at.tsvetkov.samizdat.R;
import ua.at.tsvetkov.samizdat.activities.AcTemplate;
import ua.at.tsvetkov.strings.Util;
import ua.at.tsvetkov.ui.ProgressManager;
import ua.at.tsvetkov.ui.Rout;
import ua.at.tsvetkov.util.Log;
import android.app.ProgressDialog;
import android.widget.Toast;

/**
 * Класс читающий данные по строке запроса.
 * 
 * @author A.Tsvetkov 2011, http://tsvetkov.at.ua, al@ukr.net
 */
public class DataDownloader {

	public static final boolean		TEXT									= true;
	public static final boolean		DATA									= false;
	public static final boolean		FORSED_LOADING_FROM_SD			= true;
	public static final boolean		FORSED_LOADING_FROM_INTERNET	= false;
	private static final String		ENCODING								= "windows-1251"; // "koi8-r";
	private static final String		CR										= "\n";

	private ProgressManager				pm;
	private ArrayList<CharSequence>	data;
	private String							urlStr;
	private AcTemplate					context;
	private OnCompleteLoadListener	onCompleteLoadListener;
	private String							fileName;
	private int								fullSize								= 0;
	private int								currentPartSize					= 0;
	private int								count;
	private int								currentStart;
	private ProgressDialog				progress;

	/**
	 * Инициализирует класс загрузки данных.
	 */
	public DataDownloader() {
		this.data = new ArrayList<CharSequence>();
	}

	/**
	 * Инициализирует класс загрузки данных.
	 * 
	 * @param context текущий контекст
	 * @param urlStr строка для загрузки в интернете
	 * @param count количество данных
	 */
	public DataDownloader(AcTemplate context, ProgressDialog progress, String urlStr, int count) {
		this.context = context;
		this.urlStr = urlStr;
		this.data = new ArrayList<CharSequence>();
		this.count = count;
		this.progress = progress;
	}

	/**
	 * Назначает новый адрес загрузки
	 * 
	 * @param url
	 */
	public void setUrl(String url) {
		this.urlStr = url;
	}

	/**
	 * Назначает новый прогресс диалог
	 * 
	 * @param progress
	 */
	public void setProgress(ProgressDialog progress) {
		this.progress = progress;
	}

	/**
	 * Назначает обработчик окончания загрузки
	 * 
	 * @param onCompleteLoadListener
	 */
	public void setOnCompleteLoadListener(OnCompleteLoadListener onCompleteLoadListener) {
		this.onCompleteLoadListener = onCompleteLoadListener;
	}

	/**
	 * Загрузка данных из интернета на карту.
	 * 
	 * @param fullSize количестов строк для загрузки.
	 * @param fileName имя файла для записи
	 * @param clean производить очистку строк от мусора
	 * @param reload производить обязательную перезагрузку из интернета
	 */
	public void download(final int fullSize, final String fileName, final boolean clean, final boolean reload) {
		pm = new ProgressManager(context, progress, context.getString(R.string.loading_from_internet), fullSize, 100);
		this.fileName = fileName;
		new Thread(new Runnable() {

			@Override
			public void run() {
				data.clear();
				if (NetCheck.getInstance().isOnline()) {
					onCompleteLoadListener.loadComplete(loadDataFromInetAndSave(fileName, clean, reload));
				} else {
					onCompleteLoadListener.loadComplete(partLoadFromSD(0));
					Rout.toast(context, NetCheck.getInstance().getStatusStringRu(), Toast.LENGTH_LONG);
				}
				pm.remove();
			}
		}).start();
	}

	/**
	 * Возвращает полное количество данных.
	 * 
	 * @return
	 */
	public int size() {
		return fullSize;
	}

	/**
	 * Возвращает текущий размер данных.
	 * 
	 * @return
	 */
	public int currentSize() {
		return currentPartSize;
	}

	/**
	 * Добавляет данные
	 * 
	 * @param string
	 */
	public void addData(String string) {
		data.add(string);
	}

	/**
	 * Возвращает строку данных
	 * 
	 * @param index
	 * @return
	 */
	public String getData(int index) {
		return (String) data.get(index);
	}

	/**
	 * Возвращает массив полей данных с гарантированным количеством полей.
	 * 
	 * @param index
	 * @return
	 */
	public String[] getDataFields(int index, int count) {
		return Util.split((String) data.get(index), count);
	}

	/**
	 * Загрузка данных из интернета.
	 * 
	 * @param fileName
	 * @param clean
	 * @param reload
	 */
	private boolean loadDataFromInetAndSave(String fileName, boolean clean, boolean reload) {
		Log.v("Download from url=" + urlStr);
		File file = new File(fileName);
		boolean isOld = System.currentTimeMillis() - file.lastModified() > Const.LIVE_TIME;
		if (file.exists() && (file.length() > 100) && !(reload || isOld)) {
			getFileSize();
			return partLoadFromSD(0);
		}
		BufferedReader in = null;
		InputStream is = null;
		InputStreamReader br = null;
		BufferedWriter out = null;
		pm.setTitleAndMessage(context.getString(R.string.loading), context.getString(R.string.loading_from_internet));
		try {
			HttpURLConnection connection;
			URL url = new URL(urlStr);
			connection = (HttpURLConnection) url.openConnection();
			connection.setConnectTimeout(Const.TIMEOUT);
			connection.setRequestMethod("GET");
			connection.setRequestProperty("Content-Type", "text/plain; charset=utf-8");
			connection.setRequestProperty("User-Agent", "Android reader");
			is = connection.getInputStream();
			br = new InputStreamReader(is, ENCODING);
			in = new BufferedReader(br);
			out = new BufferedWriter(new FileWriter(file));
			fullSize = 0;
			int lineCount = 0;
			while (true) {
				SoftReference<String> ref = null;
				if (clean) {
					ref = new SoftReference<String>(Util.removeHtmlTag(in.readLine()));
				} else {
					ref = new SoftReference<String>(in.readLine());
				}
				if (ref.get() == null)
					break;
				String[] sts = ref.get().split("\\|");
				SoftReference<StringBuilder> sb = new SoftReference<StringBuilder>(new StringBuilder());
				for (int i = 0; i < sts.length; i++) {
					sts[i] = sts[i].trim();
					if (sts[i].length() == 0) {
						sts[i] = " ";
					}
					sb.get().append(sts[i] + '|');// TODO не работает, мало полей дает
				}
				// End clean
				lineCount++;
				pm.increment(1);
				if (fullSize < count) {
					data.add(sb.get().toString());
				}
				fullSize++;
				out.write(sb.get().toString() + CR);
			}
			in.close();
			br.close();
			is.close();
			out.flush();
			out.close();
			if (lineCount > 0) {
				Log.v("Loaded and saved " + lineCount + " lines. Url=" + urlStr);
			} else {
				Log.v("Data is empty. Url=" + urlStr);
			}
		} catch (Exception e) {
			Log.e("Failed loading " + urlStr, e);
			return false;
		}
		return partLoadFromSD(0);
	}

	/**
	 * Возвращает количество строк в файле
	 */
	public int getFileSize() {
		BufferedReader in = null;
		fullSize = 0;
		try {
			in = new BufferedReader(new FileReader(fileName));
			data.clear();
			while (in.ready()) {
				in.readLine();
				fullSize++;
			}
			in.close();
		} catch (Exception e) {
			Log.e("Failed calculate " + fileName, e);
		}
		return fullSize;
	}

	public int currentStart() {
		return currentStart;
	}

	/**
	 * Загрузка данных с карты.
	 * 
	 * @param true если загрузка успешная
	 */
	public boolean partLoadFromSD(int currentStart) {
		Log.v("Download file=" + fileName);
		if (fileName == null) {
			onCompleteLoadListener.loadComplete(false);
			return false;
		}
		File file = new File(fileName);
		if (!file.exists()) {
			onCompleteLoadListener.loadComplete(false);
			return false;
		}
		this.currentStart = currentStart;
		int size = currentStart + count;
		pm = new ProgressManager(context, progress, context.getString(R.string.loading_from_sd), size, 100);
		BufferedReader in = null;
		int position = 0;
		currentPartSize = 0;
		try {
			in = new BufferedReader(new FileReader(file));
			data.clear();
			while (in.ready() && position <= size) {
				if (position < currentStart) {
					in.readLine();
				} else {
					data.add(in.readLine());
					currentPartSize++;
				}
				pm.increment(1);
				position++;
			}
			in.close();
		} catch (Exception e) {
			Log.e("Failed loading " + fileName, e);
			pm.remove();
			onCompleteLoadListener.loadComplete(false);
			return false;
		}
		pm.remove();
		onCompleteLoadListener.loadComplete(true);
		return true;
	}

	/**
	 * Распечатывает в логе содержимое
	 */
	public void printDataToLog() {
		for (int i = 0; i < data.size(); i++) {
			Log.v("Line " + i + ':' + data.get(i));
		}
	}

	/**
	 * @return
	 */
	public String[] getData() {
		return data.toArray(new String[data.size()]);
	}

}
